package com.ilia.util.timer.impl;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ilia.util.timer.ITimer;

public class StackTimer implements ITimer {
	
	public StackTimer() {
		stack = new ArrayList<TimerEntry>();
		pureTime = new HashMap<String,ResultEntry>();
		bruttoTime = new HashMap<String,ResultEntry>();
	}
	
	class TimerEntry{
		String name;
		long consumed;
		long started_at;
		long created_at;
	}
	
	class ResultEntry{
		ResultEntry(String name) {
			this.name = name;
		}
		String name;
		long value;
	}
	
	Map<String,ResultEntry> pureTime;
	Map<String,ResultEntry> bruttoTime;
	List<TimerEntry> stack;
	
	public void startTimer(String timerName) {
		long endTime = System.currentTimeMillis();
		TimerEntry entry = null;
		if (stack.size()>0) {
			entry = stack.get(stack.size()-1);
			long delta = endTime - entry.started_at;
			entry.consumed = entry.consumed + delta;
		}
		
		entry = new TimerEntry();
		entry.created_at = endTime;
		entry.started_at = endTime;
		entry.name = timerName;
		stack.add(entry);
	}
	
	public void stopTimer(String timerName){
		long endTime = System.currentTimeMillis();
		TimerEntry entry = stack.get(stack.size()-1);
		if (entry.name.equals(timerName)) {
			if (pureTime.get(timerName)==null) {
				pureTime.put(timerName,new ResultEntry(timerName));
				bruttoTime.put(timerName,new ResultEntry(timerName));
			}
			ResultEntry pure = pureTime.get(timerName);
			ResultEntry brutto = bruttoTime.get(timerName);
			
			long delta_pure = endTime - entry.started_at;
			long delta_brutto = endTime-entry.created_at;
			pure.value = pure.value + delta_pure + entry.consumed;
			brutto.value = brutto.value + delta_brutto;
			pureTime.put(timerName, pure);
			bruttoTime.put(timerName, brutto);
			stack.remove(stack.size()-1);
			if (stack.size()>0) {
				entry = stack.get(stack.size()-1);
				entry.started_at = endTime;
			}
		} else {
			throw new RuntimeException("timer is open: "+entry.name+" but tried to close timer "+timerName);
		}
	}

	public String toString() {
		List<ResultEntry> results = new ArrayList<ResultEntry>();
		results.addAll(bruttoTime.values());
		Collections.sort(results, new ResultEntryComparator());
		
		StringBuffer sb = new StringBuffer();
		for (ResultEntry resultEntry : results) {
			sb.append(""+resultEntry.name+": "+pureTime.get(resultEntry.name).value+"/"+bruttoTime.get(resultEntry.name).value+"\n");
		}

		long now = System.currentTimeMillis();
		if (stack.size()>0) {
			sb.append("Pending:\n");
			for (TimerEntry entry : stack) {
				sb.append("  "+entry.name+": "+(now-entry.created_at+entry.consumed)+"\n");
				
			}
		}
		return sb.toString();

	}
	
	class ResultEntryComparator implements Comparator {

		@Override
		public int compare(Object arg0, Object arg1) {
			ResultEntry e1 = (ResultEntry)arg0;
			ResultEntry e2 = (ResultEntry)arg1;
			if (e1.value<e2.value) return -1;
			if (e1.value>e2.value) return 1;
			return e1.name.compareTo(e2.name);
		}
	}

}
